package com.md.config.aspectj;

import com.fasterxml.jackson.core.type.TypeReference;
import com.md.base.exception.ErrorMessage;
import com.md.base.exception.ServerException;
import com.md.client.OpenApiClient;
import com.md.client.OpenLogsClient;
import com.md.config.Logs;
import com.md.context.LogServerContext;
import com.md.model.system.CommonResult;
import com.md.request.OperationLogsRequest;
import com.md.response.ApiResponse;
import com.md.util.ClassUtils;
import com.md.util.HttpUtil;
import com.md.util.ObjectUtil;
import com.md.util.StringUtil;
import com.md.util.ThreadUtil;
import com.md.value.constants.DefaultServerContext;
import com.md.value.enums.ErrorEnum;
import com.md.value.server.ServerContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.reflect.MethodSignature;

import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Optional;
import java.util.concurrent.Executor;
import java.util.stream.IntStream;

import static com.md.value.LogsConstants.CREATE_LOG_THREAD_NAME;
import static com.md.value.LogsConstants.MAX_THREAD_SIZE;
import static com.md.value.LogsConstants.SERVER_CONTEXT_KEY_DATA_ID;
import static com.md.value.LogsConstants.SERVER_CONTEXT_KEY_IP_ADDRESS;
import static com.md.value.LogsConstants.SERVER_CONTEXT_KEY_PARENT_ID;

/**
 * @author 刘骄阳
 * 2024-10-11 11:14
 */
@Slf4j
@RequiredArgsConstructor
public class LogsAspectj {

  // todo 线程池等待策略
  private final Executor executor = ThreadUtil.newInstanceExecutor(MAX_THREAD_SIZE, CREATE_LOG_THREAD_NAME);

  /**
   * 环绕通知
   *
   * @param proceedingJoinPoint proceedingJoinPoint
   * @return 返回结果
   */
  @Around("@annotation(com.md.config.annotation.Logs)")
  public CommonResult<Object> around(final ProceedingJoinPoint proceedingJoinPoint) {
    CommonResult<Object> proceed;
    OperationLogsRequest request = buildRequest(proceedingJoinPoint);
    LocalDateTime startDate = LocalDateTime.now();
    try {
      Object response = proceedingJoinPoint.proceed();
      String json = StringUtil.objectToJson(response);
      proceed = StringUtil.jsonToObject(json, new TypeReference<CommonResult<Object>>() {
      });
    } catch (Throwable e) {
      if (e instanceof ServerException) {
        ServerException serverException = ((ServerException) e);
        proceed = CommonResult.error(ErrorMessage.of(serverException.getCode(), serverException.getMessage()));
      } else {
        log.error("发生异常: " + e.getMessage(), e);
        proceed = CommonResult.error(ErrorEnum.SYSTEM_ERROR);
      }
      // 发生异常获取异常的值
      request.setErrorMsg(e.getMessage());
    } finally {
      LocalDateTime endDate = LocalDateTime.now();
      // 获取执行时间
      request.setConsumingTime(Duration.between(startDate, endDate).toMillis());
      ThreadUtil.runAsync(() -> OpenLogsClient.recordOperatorLog(request), "新增日志", executor);
      // todo 增加慢日志的处理，消息通知
      // todo 增加日志失败处理，写入本地文件，启动定时任务定时上报
    }
    return proceed;
  }

  private String getRequestParam(final ProceedingJoinPoint proceedingJoinPoint) {
    MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
    Object[] args = proceedingJoinPoint.getArgs();
    String[] parameterNames = signature.getParameterNames();
    int length = parameterNames.length;
    StringBuilder sb = new StringBuilder();
    sb.append("{");
    IntStream.range(0, length).forEach(i -> {
      String json = StringUtil.objectToJson(args[i]);
      sb.append("\"").append(parameterNames[i]).append("\":").append(json);
      if (i != length - 1) {
        sb.append(",");
      }
    });
    sb.append("}");
    return sb.toString();
  }

  private OperationLogsRequest buildRequest(final ProceedingJoinPoint proceedingJoinPoint) {
    Logs annotation = ClassUtils.getAnnotation(proceedingJoinPoint, Logs.class);
    OperationLogsRequest request = new OperationLogsRequest();
    request.setTitle(annotation.title());
    request.setOperationContent(annotation.operationContent());
    request.setOperateDate(LocalDateTime.now());
    // 设置枚举的值
    request.setOperation(annotation.operation());
    request.setParentId(LogServerContext.getString(SERVER_CONTEXT_KEY_PARENT_ID));
    request.setDataId(LogServerContext.getString(SERVER_CONTEXT_KEY_DATA_ID));
    request.setIpAddress(LogServerContext.getString(SERVER_CONTEXT_KEY_IP_ADDRESS));
    // 获取请求参数
    request.setRequestParam(getRequestParam(proceedingJoinPoint));
    request.setTenantId(ServerContext.getLong(DefaultServerContext.TENANT_ID));
    request.setBrandId(ServerContext.getLong(DefaultServerContext.BRAND_ID));

    HttpServletRequest httpServletRequest = HttpUtil.getHttpServletRequest();

    Optional.ofNullable(httpServletRequest).ifPresent(e -> {
      request.setUrl(e.getRequestURI());
      ApiResponse apiResponse = OpenApiClient.queryApi(httpServletRequest.getRequestURI(),
        httpServletRequest.getMethod());
      if (ObjectUtil.hasEmpty(request.getTenantId(), request.getBrandId())) {
        request.setTenantId(apiResponse.getTenantId());
        request.setBrandId(apiResponse.getBrandId());
      }
    });
    return request;
  }
}
